Completly new, data driven XSV handlers. Thanx, Alex!
authorrobertl <robertl@f51c46e8-681c-474f-0cfe-069cfd0219fb>
Wed, 8 Jan 2003 16:06:36 +0000 (16:06 +0000)
committerrobertl <robertl@f51c46e8-681c-474f-0cfe-069cfd0219fb>
Wed, 8 Jan 2003 16:06:36 +0000 (16:06 +0000)
25 files changed:
gpsbabel/Makefile
gpsbabel/README
gpsbabel/README.xmapwpt [new file with mode: 0644]
gpsbabel/csv_util.c
gpsbabel/csv_util.h
gpsbabel/defs.h
gpsbabel/mxf.c
gpsbabel/ozi.c
gpsbabel/reference/xmapwpt.wpt [new file with mode: 0644]
gpsbabel/style/README.style [new file with mode: 0644]
gpsbabel/style/csv.style [new file with mode: 0644]
gpsbabel/style/custom.style [new file with mode: 0644]
gpsbabel/style/dna.style [new file with mode: 0644]
gpsbabel/style/gpsdrive.style [new file with mode: 0644]
gpsbabel/style/gpsman.style [new file with mode: 0644]
gpsbabel/style/mxf.style [new file with mode: 0644]
gpsbabel/style/nima.style [new file with mode: 0644]
gpsbabel/style/ozi.style [new file with mode: 0644]
gpsbabel/style/s_and_t.style [new file with mode: 0644]
gpsbabel/style/tiger.style [new file with mode: 0644]
gpsbabel/style/xmap.style [new file with mode: 0644]
gpsbabel/testo
gpsbabel/vecs.c
gpsbabel/xcsv.c [new file with mode: 0644]
gpsbabel/xmapwpt.c [new file with mode: 0644]

index 6f5fb0dea1c0115ce51756a272608543ebc8f78f..f1a549c0d294149532d64c9ed949588cbb3cb600 100644 (file)
@@ -2,7 +2,8 @@ CFLAGS=-g -Icoldsync
 
 FMTS=magproto.o gpx.o geo.o gpsman.o mapsend.o mapsource.o \
        gpsutil.o tiger.o pcx.o csv.o cetus.o gpspilot.o magnav.o \
-       psp.o mxf.o holux.o garmin.o ozi.o tmpro.o dna.o tpg.o gpsdrive.o
+       psp.o mxf.o holux.o garmin.o ozi.o tmpro.o dna.o tpg.o gpsdrive.o \
+       xcsv.o xmapwpt.o
 
 JEEPS=jeeps/gpsapp.o jeeps/gpscom.o jeeps/gpsfmt.o jeeps/gpsinput.o \
        jeeps/gpsmath.o jeeps/gpsmem.o  \
@@ -64,6 +65,8 @@ main.o: main.c defs.h queue.h
 mapsend.o: mapsend.c defs.h queue.h mapsend.h
 mapsource.o: mapsource.c defs.h queue.h
 mkshort.o: mkshort.c defs.h queue.h
+xmapwpt.o: mxf.c defs.h queue.h csv_util.h
+xcsv.o: xcsv.c defs.h queue.h csv_util.h
 mxf.o: mxf.c defs.h queue.h csv_util.h
 ozi.o: ozi.c defs.h queue.h csv_util.h
 pcx.o: pcx.c defs.h queue.h
index 31b8419a8d854401cf422f61343da349f1ffd633..cab201249e5293d8c34b82dbae2be67b2e760e4f 100644 (file)
@@ -100,6 +100,44 @@ THE FORMATS
        This is the format used to hot-sync to XMap from withing TopoUSA.
        Done with help of Dan Edwards.
 
+    XMapWpt
+
+       Delorme XMapHandHeld Street Atlas USA is another of the billion 
+       CSV variants.  This is the format used by XmapHH SA USA on
+       (at least) PocketPC O/S.  Please see README.xmapwpt for more 
+       information on it's intricacies.  This XMap is not to be confused
+       with the XMap mentioned above. Contributed to gpsbabel by
+       Alex Mottram.
+
+    XCSV
+
+       XCSV is an open-ended "Whatever Separated Values" parser / writer
+       designed to work with user-supplied "style" files.  It should handle 
+       at least a few thousand of the billion CSV variants available.  
+       By itself, it doesn't comply to any format, however *most* CSV 
+       variants can be described as a "style" and fine-tuned by the end 
+       user.  For more information on it's use, please see README.style 
+       in the style/ sub-directory of gpsbabel.  For an example of using 
+       the XCSV module within your C program, look at the ozi.c, mxf.c, and
+       xmapwpt.c sources in the gpsbabel directory.  This module was 
+       contributed to gpsbabel by Alex Mottram.
+
+       Additional Options:
+       style   -       **REQUIRED** Path to XCSV style file.
+
+       snlen   -       Maximum length of synthesized shortnames. 
+       snwhite -       Switch defining whether or not to allow whitespace
+                       in synthesized shortnames. 
+                       (0 = NO WHITESPACE, 1 = WHITESPACE OK).
+       snupper -       Switch defining whether or not to force uppercase 
+                       in shortnames. (0 = LEAVE AS IS, 1 = UPPERCASE ALL).
+       
+       NOTE:   sn* options require use of the '-s' command line option.
+
+       Example Usage:
+       gpsbabel -i xsv,style=foo.style -f foo -o xsv,style=bar.style -F bar
+       gpsbabel -s -i gpx -f foo.gpx -o xsv,style=my.style,snlen=8 -F bar
+
     MAPSEND
 
        Magellan was smart enough to document their file format to make
diff --git a/gpsbabel/README.xmapwpt b/gpsbabel/README.xmapwpt
new file mode 100644 (file)
index 0000000..6e8539c
--- /dev/null
@@ -0,0 +1,66 @@
+
+NOTE: THIS README PERTAINS TO THE "XMAPWPT" FORMAT, NOT THE XMAP/TOPO USA
+      4.0 CONDUIT "XMAP" FORMAT.
+
+
+Delorme XMap Handheld .WPT for PocketPC is a bit of a kludge.  This 
+document covers XMap Handheld Street Atlas USA edition.
+
+XMap on the PocketPC stores it's waypoints in individual .wpt files.
+For example, waypoints generated by XMap on the PocketPC are stored
+by default in the "My Documents" folder using the sequential names
+"XMap1.wpt", "XMap2.wpt", ad nauseum.  Needless to say, not very
+efficient.
+
+As writing multiple waypoint files is outside of the scope of gpsbabel,
+gpsbabel chooses to write one big file, one waypoint per line.  
+Extracting lines from this file is left as an exercise for the end user.  
+A simple perl script to handle this conversion is included at the end 
+of this README.
+
+It should also be noted that READING multiple files is indeed possible,
+but if you have more than a few points, it can be a task.  For example:
+
+gpsbabel -i xmapwpt -f Xmap1.wpt -f Xmap2.wpt -o mapsend -F mapsend.wpt
+
+will read the two Xmap .wpt files and write one mapsend file.  This
+is fine for a small handful of points, but could be quite cumbersome
+for folks like me who have 100+ waypoints loaded into XMap.  For *nix
+folks, something as simple as:
+
+cat *.wpt > /tmp/foo.wpt
+gpsbabel -i xmapwpt -f foo.wpt -o mapsend -F mapsend.wpt 
+
+will do the trick just fine.  
+
+
+############ BEGIN SCRIPT 
+#!/full/path/to/perl
+$INPUTFILE = @ARGV[0];
+$TARGETDIR = @ARGV[1];
+$FILENAME  = @ARGV[2];
+
+if (! $FILENAME) {
+    print "Usage: xmap_split.pl INPUT_FILE OUTPUT_DIRECTORY FILENAME_BASE\n";
+    print " (i.e. xmapl_split.pl points.wpt /tmp/points GPSB)\n";
+    print " (created GPSB0001-GPSBXXXX in /tmp/points/ from points.wpt)\n";
+    exit;
+}
+
+open (INFILE, $INPUTFILE) || die "Cannot open $INPUTFILE for read!\n";
+
+while (<INFILE>) {
+    $lc++;
+    $filename = sprintf("%s/Gpsb%04d.wpt", $TARGETDIR, $lc);
+
+    open (OUTFILE, ">$filename") || die "Cannot open $filename for write!\n";
+
+    print OUTFILE $_;
+
+    close(OUTFILE);
+}
+
+exit;
+
+########### END SCRIPT
+
index e7687b5a4a0dd15f981d07d1952c96b66137e131..03fe2f880712b2f916e3b0f60ea7663ae8afcec4 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Utilities for parsing Comma Seperated Value files (CSV)
+    Utilities for parsing Character Separated Value files (CSV)
 
     Copyright (C) 2002 Alex Mottram (geo_alexm at cox-internet.com)
 
 
 #define MYNAME "CSV_UTIL"
 
+/* macros */
+#define LAT_DIR(a) a < 0.0 ? 'S' : 'N'
+#define LON_DIR(a) a < 0.0 ? 'W' : 'E'
+
+/* convert excel time (days since 1900) to time_t and back again */
+#define EXCEL_TO_TIMET(a) ((a - 25569.0) * 86400.0)
+#define TIMET_TO_EXCEL(a) ((a / 86400.0) + 25569.0)
+
 /*********************************************************************/
 /* csv_stringclean() - remove any unwanted characters from string.   */
 /*                     returns copy of string.                       */
@@ -132,7 +140,7 @@ csv_lineparse(const char *stringstart, const char *delimited_by,
     const char *sp;
     static const char *p = NULL;
     static char *tmp = NULL;
-    size_t dlen, elen;
+    size_t dlen = 0, elen = 0;
     int enclosedepth = 0;
     short int dfound;
 
@@ -155,8 +163,10 @@ csv_lineparse(const char *stringstart, const char *delimited_by,
     sp = p;
 
     /* length of delimiters and enclosures */
-    dlen = strlen(delimited_by);
-    elen = strlen(enclosed_in);
+    if (delimited_by) 
+        dlen = strlen(delimited_by);
+    if (enclosed_in)
+        elen = strlen(enclosed_in);
     
     dfound = 0;
 
@@ -170,7 +180,6 @@ csv_lineparse(const char *stringstart, const char *delimited_by,
 
        if ((!enclosedepth) && (strncmp(p, delimited_by, dlen) == 0)) {
            dfound = 1;
-
        } else {
            p++;
        }
@@ -198,3 +207,582 @@ csv_lineparse(const char *stringstart, const char *delimited_by,
     return (tmp);
 }
 
+/*****************************************************************************/
+/* dec_to_intdeg() - convert decimal degrees to integer degreees             */
+/*    usage: i = dec_to_intdeg(31.1234, 1);                                  */
+/*           i = dec_to_intdeg(91.1234, 0);                                  */
+/*****************************************************************************/
+static int
+dec_to_intdeg(const double d, const int islat) 
+{
+    int ideg = 0;
+    
+    if (islat) {
+        ideg = (2147483647) - (d * 8388608);
+    } else {
+        ideg = (2147483647) - (fabs(d) * 8388608) + 1; 
+    }
+
+    return(ideg);
+}
+
+/*****************************************************************************/
+/* intdeg_to_dec() - convert integer degrees to decimal degreees             */
+/*    usage: lat = dec_to_intdeg(ilat, 1);                                   */
+/*           lon = dec_to_intdeg(ilon, 0);                                   */
+/*****************************************************************************/
+static double
+intdeg_to_dec(const int ideg, const int islat) 
+{
+    double d;
+    
+    if (islat) {
+        d = ((2147483647) - ideg) / (double)8388608;
+    } else {
+        d = ((-2147483647-1) + ideg) / (double)8388608;
+    }
+
+    return(d);
+}
+
+/*****************************************************************************/
+/* decdir_to_dec() - convert a decimal/direction value into pure decimal.    */
+/* usage: lat = decdir_to_dec("W90.1234");                                   */
+/*        lat = decdir_to_dec("30.1234N");                                  */
+/*****************************************************************************/
+static double 
+decdir_to_dec(const char * decdir) 
+{
+    char *p;
+    const char *cp;
+    double rval;
+    int sign = 0;
+    
+    cp = &decdir[0];
+    
+    if ((*cp == 'W') || (*cp == 'S')) 
+        sign = -1;
+    else 
+    if ((*cp == 'N') || (*cp == 'E')) 
+        sign = 1;
+    
+    rval = sign ? strtod(&decdir[1], &p) : strtod(&decdir[0], &p);
+    
+    if (sign == 0) {
+        if ((*p == 'W') || (*p == 'S')) 
+            sign = -1;
+        else 
+        if ((*p == 'N') || (*p == 'E')) 
+            sign = 1;
+    }
+    
+    return(rval * sign);
+}
+
+/*****************************************************************************/
+/* xcsv_file_init() - prepare xcsv_file for first use.                       */
+/*****************************************************************************/
+void
+xcsv_file_init(void)
+{
+    memset(&xcsv_file, '\0', sizeof(xcsv_file_t));
+
+    QUEUE_INIT(&xcsv_file.prologue);
+    QUEUE_INIT(&xcsv_file.epilogue);
+
+    QUEUE_INIT(&xcsv_file.ifield);
+    /* ofield is alloced to allow pointing back at ifields
+     * where applicable.
+     */
+    xcsv_file.ofield = xcalloc(sizeof(queue), 1);
+    QUEUE_INIT(xcsv_file.ofield);
+}
+
+/*****************************************************************************/
+/* xcsv_ifield_add() - add input field to ifield queue.                      */
+/* usage: xcsv_ifield_add("DESCRIPTION", "", "%s")                           */
+/*****************************************************************************/
+void
+xcsv_ifield_add(char *key, char *val, char *pfc)
+{
+    field_map_t *fmp = xcalloc(sizeof(*fmp), 1);
+    
+    fmp->key = key;
+    fmp->val = val;
+    fmp->printfc = pfc;
+    
+    ENQUEUE_TAIL(&xcsv_file.ifield, &fmp->Q);
+    xcsv_file.ifield_ct++;
+}
+
+/*****************************************************************************/
+/* xcsv_ofield_add() - add output field to ofield queue.                     */
+/* usage: xcsv_ofield_add("LAT_DECIMAL", "", "%08.5lf")                      */
+/*****************************************************************************/
+void
+xcsv_ofield_add(char *key, char *val, char *pfc)
+{
+    field_map_t *fmp = xcalloc(sizeof(*fmp), 1);
+    
+    fmp->key = key;
+    fmp->val = val;
+    fmp->printfc = pfc;
+    
+    ENQUEUE_TAIL(xcsv_file.ofield, &fmp->Q);
+    xcsv_file.ofield_ct++;
+}
+
+/*****************************************************************************/
+/* xcsv_prologue_add() - add prologue line to prologue queue                 */
+/* usage: xcsv_prologue_add("Four score and seven years ago today,")         */
+/*****************************************************************************/
+void
+xcsv_prologue_add(char *prologue)
+{
+    ogue_t* ogp = xcalloc(sizeof(*ogp), 1);
+
+    ogp->val = prologue;
+    ENQUEUE_TAIL(&xcsv_file.prologue, &ogp->Q);
+    xcsv_file.prologue_lines++;
+}
+
+/*****************************************************************************/
+/* xcsv_epilogue_add() - add epilogue line to epilogue queue                 */
+/* usage: xcsv_epilogue_add("shall not perish from the earth.")              */
+/*****************************************************************************/
+void
+xcsv_epilogue_add(char *epilogue)
+{
+    ogue_t * ogp = xcalloc(sizeof(*ogp), 1);
+
+    ogp->val = epilogue;
+    ENQUEUE_TAIL(&xcsv_file.epilogue, &ogp->Q);
+    xcsv_file.epilogue_lines++;
+}
+
+/*****************************************************************************/
+/* xcsv_parse_val() - parse incoming data into the waypt structure.          */
+/* usage: xcsv_parse_val("-123.34", *waypt, *field_map)                      */
+/*****************************************************************************/
+static void
+xcsv_parse_val(const char *s, waypoint *wpt, const field_map_t *fmp)
+{
+    if (strcmp(fmp->key, "IGNORE") == 0) {
+       /* IGNORE -- Categorically ignore this... */
+    } else
+    if (strcmp(fmp->key, "CONSTANT") == 0) {
+       /* CONSTANT -- Ignore on Input... */
+    } else
+    if (strcmp(fmp->key, "ANYNAME") == 0) {
+       /* ANYNAME -- Ignore -- this is output magic. */
+    } else
+    if (strcmp(fmp->key, "INDEX") == 0) {
+       /* IGNORE -- Calculated Sequence # For Ouput*/
+    } else
+    if (strcmp(fmp->key, "SHORTNAME") == 0) {
+       wpt->shortname = csv_stringtrim(s, "");
+    } else
+    if (strcmp(fmp->key, "DESCRIPTION") == 0) {
+       wpt->description = csv_stringtrim(s, "");
+    } else
+    if (strcmp(fmp->key, "NOTES") == 0) {
+       wpt->notes = csv_stringtrim(s, "");
+    } else
+    if (strcmp(fmp->key, "URL") == 0) {
+       wpt->url = csv_stringtrim(s, "");
+    } else
+    if (strcmp(fmp->key, "URL_LINK_TEXT") == 0) {
+       wpt->url_link_text = csv_stringtrim(s, "");
+    } else
+    if (strcmp(fmp->key, "ICON_DESCR") == 0) {
+       wpt->icon_descr = csv_stringtrim(s, "");
+    } else
+
+    /* LATITUDE CONVERSIONS**************************************************/
+    if (strcmp(fmp->key, "LAT_DECIMAL") == 0) {
+       /* latitude as a pure decimal value */
+       wpt->position.latitude.degrees = atof(s);
+    } else
+    if ((strcmp(fmp->key, "LAT_DECIMALDIR") == 0) ||
+        (strcmp(fmp->key, "LAT_DIRDECIMAL") == 0)) {
+       /* latitude as a decimal with N/S in it. */
+       wpt->position.latitude.degrees = decdir_to_dec(s);
+    } else
+    if (strcmp(fmp->key, "LAT_INT32DEG") == 0) {
+       /* latitude as a 32 bit integer offset */
+       wpt->position.latitude.degrees = intdeg_to_dec(atof(s), 1);
+    } else
+    /* LONGITUDE CONVERSIONS ***********************************************/
+    if (strcmp(fmp->key, "LON_DECIMAL") == 0) {
+       /* longitude as a pure decimal value */
+       wpt->position.longitude.degrees = atof(s);
+    } else
+    if ((strcmp(fmp->key, "LON_DECIMALDIR") == 0) ||
+        (strcmp(fmp->key, "LON_DIRDECIMAL") == 0)) {
+       /* longitude as a decimal with N/S in it. */
+       wpt->position.longitude.degrees = decdir_to_dec(s);
+    } else
+    if (strcmp(fmp->key, "LON_INT32DEG") == 0) {
+       /* longitude as a 32 bit integer offset  */
+       wpt->position.longitude.degrees = intdeg_to_dec(atof(s), 0);
+    } else
+
+    /* DIRECTIONS **********************************************************/
+    if (strcmp(fmp->key, "LAT_DIR") == 0) {
+       /* latitude N/S.  Ignore on input for now */
+    } else
+    if (strcmp(fmp->key, "LON_DIR") == 0) {
+       /* longitude E/W. Ingore on input for now */
+    } else
+
+    /* ALTITUDE CONVERSIONS ************************************************/
+    if (strcmp(fmp->key, "ALT_FEET") == 0) {
+       /* altitude in feet as a decimal value */
+       wpt->position.altitude.altitude_meters = atof(s) * .3048;
+    } else
+    if (strcmp(fmp->key, "ALT_METERS") == 0) {
+       /* altitude in meters as a decimal value */
+       wpt->position.altitude.altitude_meters = atof(s);
+    } else
+
+    /* TIME CONVERSIONS ***************************************************/
+    if (strcmp(fmp->key, "EXCEL_TIME") == 0) {
+       /* Time as Excel Time  */
+       wpt->creation_time = EXCEL_TO_TIMET(atof(s));
+    } else
+    if (strcmp(fmp->key, "TIMET_TIME") == 0) {
+       /* Time as time_t */
+       wpt->creation_time = atol(s);
+     } else
+
+    /* GEOCACHING STUFF ***************************************************/
+    if (strcmp(fmp->key, "GEOCACHE_DIFF") == 0) {
+       /* Geocache Difficulty as an int */
+       wpt->gc_data.diff = atof(s) * 10; 
+    } else
+    if (strcmp(fmp->key, "GEOCACHE_TERR") == 0) {
+       /* Geocache Terrain as an int */
+       wpt->gc_data.terr = atof(s) * 10;
+    } else
+    if (strcmp(fmp->key, "GEOCACHE_TYPE") == 0) {
+       /* Geocache Type - TODO */
+       /* wpt->gc_data.type = gs_mktype(s);*/
+
+    } else {
+       fprintf (stderr, "UNKNOWN STYLE DIRECTIVE: %s\n", fmp->key);
+    }
+}
+
+/*****************************************************************************/
+/* xcsv_data_read() - read input file, parsing lines, fields and handling    */
+/*                   any data conversion (the input meat)                    */
+/*****************************************************************************/
+void
+xcsv_data_read(void)
+{
+    char buff[8192];
+    char *s;
+    waypoint *wpt_tmp;
+    int linecount = 0;
+    queue *elem, *tmp;
+    field_map_t *fmp;
+    ogue_t *ogp;
+
+    do {
+        linecount++;
+        memset(buff, '\0', sizeof(buff));
+        fgets(buff, sizeof(buff), xcsv_file.xcsvfp);
+
+        rtrim(buff);
+
+        /* skip over x many lines on the top for the prologue... */
+        if ((xcsv_file.prologue_lines) && ((linecount - 1) <
+          xcsv_file.prologue_lines)) {
+            continue;
+        }
+
+        /* We should skip over epilogue lines also.  Since we don't want to 
+         * pre-read the file to know how many data lines we should be seeing, 
+         * we take this cheap shot at the data and cross our fingers.
+         */
+
+        QUEUE_FOR_EACH(&xcsv_file.epilogue, elem, tmp) {
+            ogp = (ogue_t *) elem;
+            if (strncmp(buff, ogp->val, strlen(ogp->val)) == 0) {
+                buff[0] = '\0';
+                break;
+            }
+        }
+
+        if (strlen(buff)) {
+            wpt_tmp = xcalloc(sizeof(*wpt_tmp), 1);
+
+            s = buff;
+            s = csv_lineparse(s, xcsv_file.field_delimiter, "", linecount);
+
+            /* reset the ifield queue */
+            elem = QUEUE_FIRST(&xcsv_file.ifield);
+
+            /* now rip the line apart, advancing the queue for each tear
+             * off the beginning of buff since there's no index into queue.
+             */
+            while (s) {
+                fmp = (field_map_t *) elem;
+                xcsv_parse_val(s, wpt_tmp, fmp);
+
+                elem = QUEUE_NEXT(elem);
+                
+                if (elem == &xcsv_file.ifield) {
+                    /* we've wrapped the queue. so stop parsing! */
+                    break;
+                }
+
+                s = csv_lineparse(NULL, xcsv_file.field_delimiter, "",
+                  linecount);
+            }
+            waypt_add(wpt_tmp);
+        }
+
+    } while (!feof(xcsv_file.xcsvfp));
+}
+
+/*****************************************************************************/
+/* xcsv_waypt_pr() - write output file, handling output conversions          */
+/*                  (the output meat)                                        */
+/*****************************************************************************/
+static void
+xcsv_waypt_pr(const waypoint *wpt)
+{
+    char buff[1024];
+    static int index = 0;
+    char *shortname = NULL;
+    char *description = NULL;
+    char * anyname = NULL;
+    int i;
+    field_map_t *fmp;
+    queue *elem, *tmp;
+   
+    if (wpt->shortname) {
+        anyname = xstrdup(wpt->shortname);
+    } else
+    if (wpt->description) {
+        anyname = xstrdup(wpt->description);
+    } else
+    if (wpt->notes) {
+        anyname = xstrdup(wpt->notes);
+    }  else 
+        anyname = xstrdup("");
+        
+    if ((anyname) && (global_opts.synthesize_shortnames)) {
+        anyname = mkshort(anyname);
+    }
+    
+    if ((! wpt->shortname) || (global_opts.synthesize_shortnames)) {
+        if (wpt->description) {
+            if (global_opts.synthesize_shortnames)
+                shortname = mkshort(wpt->description);
+            else
+                shortname = csv_stringclean(wpt->description, xcsv_file.badchars);
+        } else {
+            /* no description available */
+            shortname = xstrdup("");
+        }
+    } else{
+        shortname = csv_stringclean(wpt->shortname, xcsv_file.badchars);
+    }
+
+    if (! wpt->description) {
+        if (shortname) {
+            description = csv_stringclean(shortname, xcsv_file.badchars);
+        } else {
+            description = xstrdup("");
+        }
+    } else {
+        description = csv_stringclean(wpt->description, xcsv_file.badchars);
+    }
+
+    i = 0;
+    QUEUE_FOR_EACH(xcsv_file.ofield, elem, tmp) {
+        fmp = (field_map_t *) elem;
+
+        if (i != 0) 
+            fprintf (xcsv_file.xcsvfp, xcsv_file.field_delimiter);
+
+        i++;
+
+        if (strcmp(fmp->key, "IGNORE") == 0) {
+            /* IGNORE -- Write the char printf conversion */
+            snprintf (buff, sizeof(buff), fmp->printfc, "");
+        } else
+        if (strcmp(fmp->key, "INDEX") == 0) {
+            snprintf (buff, sizeof(buff), fmp->printfc, index + atoi(fmp->val));
+        } else
+        if (strcmp(fmp->key, "CONSTANT") == 0) {
+            snprintf (buff, sizeof(buff), fmp->printfc, fmp->val);
+        } else
+        if (strcmp(fmp->key, "SHORTNAME") == 0) {
+            snprintf (buff, sizeof(buff), fmp->printfc, shortname);
+        } else
+        if (strcmp(fmp->key, "ANYNAME") == 0) {
+            snprintf (buff, sizeof(buff), fmp->printfc, anyname);
+        } else
+        if (strcmp(fmp->key, "DESCRIPTION") == 0) {
+            snprintf (buff, sizeof(buff), fmp->printfc, description);
+        } else
+        if (strcmp(fmp->key, "NOTES") == 0) {
+            snprintf (buff, sizeof(buff), fmp->printfc, wpt->notes);
+        } else
+        if (strcmp(fmp->key, "URL") == 0) {
+            snprintf (buff, sizeof(buff), fmp->printfc, wpt->url);
+        } else
+        if (strcmp(fmp->key, "URL_LINK_TEXT") == 0) {
+            snprintf (buff, sizeof(buff), fmp->printfc, wpt->url_link_text);
+        } else
+        if (strcmp(fmp->key, "ICON_DESCR") == 0) {
+            snprintf (buff, sizeof(buff), fmp->printfc, wpt->icon_descr);
+        } else
+
+        /* LATITUDE CONVERSION***********************************************/
+        if (strcmp(fmp->key, "LAT_DECIMAL") == 0) {
+            /* latitude as a pure decimal value */
+            snprintf (buff, sizeof(buff), fmp->printfc,
+              wpt->position.latitude.degrees);
+        } else
+        if (strcmp(fmp->key, "LAT_DECIMALDIR") == 0) {
+            /* latitude as a decimal value with N/S after it */
+            snprintf (buff, sizeof(buff), fmp->printfc,
+              fabs(wpt->position.latitude.degrees), 
+              LAT_DIR(wpt->position.latitude.degrees));
+        } else
+        if (strcmp(fmp->key, "LAT_DIRDECIMAL") == 0) {
+            /* latitude as a decimal value with N/S before it */
+            snprintf (buff, sizeof(buff), fmp->printfc,
+              LAT_DIR(wpt->position.latitude.degrees),
+              fabs(wpt->position.latitude.degrees));
+        } else
+        if (strcmp(fmp->key, "LAT_INT32DEG") == 0) {
+            /* latitude as an integer offset from 0 degrees */
+            snprintf (buff, sizeof(buff), fmp->printfc,
+              dec_to_intdeg(wpt->position.latitude.degrees, 1));
+        } else
+
+        /* LONGITUDE CONVERSIONS*********************************************/
+        if (strcmp(fmp->key, "LON_DECIMAL") == 0) {
+            /* longitude as a pure decimal value */
+            snprintf (buff, sizeof(buff), fmp->printfc,
+              wpt->position.longitude.degrees);
+        } else
+        if (strcmp(fmp->key, "LON_DECIMALDIR") == 0) {
+            /* latitude as a decimal value with N/S after it */
+            snprintf (buff, sizeof(buff), fmp->printfc,
+              fabs(wpt->position.longitude.degrees), 
+              LON_DIR(wpt->position.longitude.degrees));
+        } else
+        if (strcmp(fmp->key, "LON_DIRDECIMAL") == 0) {
+            /* latitude as a decimal value with N/S before it */
+            snprintf (buff, sizeof(buff), fmp->printfc,
+              LON_DIR(wpt->position.longitude.degrees),
+              fabs(wpt->position.longitude.degrees));
+        } else
+        if (strcmp(fmp->key, "LON_INT32DEG") == 0) {
+            /* longitudee as an integer offset from 0 degrees */
+            snprintf (buff, sizeof(buff), fmp->printfc,
+              dec_to_intdeg(wpt->position.longitude.degrees, 0));
+        } else
+
+        /* DIRECTIONS *******************************************************/
+        if (strcmp(fmp->key, "LAT_DIR") == 0) {
+            /* latitude N/S as a char */
+            snprintf (buff, sizeof(buff), fmp->printfc,
+              LAT_DIR(wpt->position.latitude.degrees));
+        } else
+        if (strcmp(fmp->key, "LON_DIR") == 0) {
+            /* longitude E/W as a char */
+            snprintf (buff, sizeof(buff), fmp->printfc,
+              LON_DIR(wpt->position.longitude.degrees));
+        } else
+
+        /* ALTITUDE CONVERSIONS**********************************************/
+        if (strcmp(fmp->key, "ALT_FEET") == 0) {
+            /* altitude in feet as a decimal value */
+            snprintf (buff, sizeof(buff), fmp->printfc,
+              (wpt->position.altitude.altitude_meters * 3.2808));
+        } else
+        if (strcmp(fmp->key, "ALT_METERS") == 0) {
+            /* altitude in meters as a decimal value */
+            snprintf (buff, sizeof(buff), fmp->printfc,
+              wpt->position.altitude.altitude_meters);
+        } else
+
+        /* TIME CONVERSIONS**************************************************/
+        if (strcmp(fmp->key, "EXCEL_TIME") == 0) {
+            /* creation time as an excel (double) time */
+            snprintf (buff, sizeof(buff), fmp->printfc,
+              TIMET_TO_EXCEL(wpt->creation_time));
+        } else
+        if (strcmp(fmp->key, "TIMET_TIME") == 0) {
+            /* time as a time_t variable */
+            snprintf (buff, sizeof(buff), fmp->printfc, wpt->creation_time);
+        } else
+
+        /* GEOCACHE STUFF **************************************************/
+        if (strcmp(fmp->key, "GEOCACHE_DIFF") == 0) {
+            /* Geocache Difficulty as a double */
+            snprintf (buff, sizeof(buff), fmp->printfc,
+              wpt->gc_data.diff / 10);
+        } else
+        if (strcmp(fmp->key, "GEOCACHE_TERR") == 0) {
+            /* Geocache Terrain as a double */
+            snprintf (buff, sizeof(buff), fmp->printfc,
+              wpt->gc_data.terr / 10);
+        } else
+        if (strcmp(fmp->key, "GEOCACHE_TYPE") == 0) {
+            /* Geocache Type TODO */
+            snprintf (buff, sizeof(buff), fmp->printfc, "Geocache");
+        } else {
+           /* this should probably never happen */
+        }
+
+        fprintf (xcsv_file.xcsvfp, "%s", buff);
+
+    }
+
+    fprintf (xcsv_file.xcsvfp, "%s", xcsv_file.record_delimiter);
+
+    if (shortname)
+        free(shortname);
+
+    if (description)
+        free(description);
+
+    if (anyname)
+        free(anyname);
+
+    index++;
+}
+
+/*****************************************************************************/
+/* xcsv_data_write(void) - write prologues, spawn the output loop, and write */
+/*                         epilogues.                                        */
+/*****************************************************************************/
+void
+xcsv_data_write(void)
+{
+    queue *elem, *tmp;
+    ogue_t *ogp;
+
+    /* output prologue lines, if any. */
+    QUEUE_FOR_EACH(&xcsv_file.prologue, elem, tmp) {
+        ogp = (ogue_t *) elem;
+        fprintf (xcsv_file.xcsvfp, "%s%s", ogp->val, xcsv_file.record_delimiter);
+    }
+
+    waypt_disp_all(xcsv_waypt_pr);
+
+    /* output epilogue lines, if any. */
+    QUEUE_FOR_EACH(&xcsv_file.epilogue, elem, tmp) {
+        ogp = (ogue_t *) elem;
+        fprintf (xcsv_file.xcsvfp, "%s%s", ogp->val, xcsv_file.record_delimiter);
+    }
+}
+
index 5d2660bf8ddff08432aecc774ae996ed002da185..2fdffed3224264e1e7fc51334c41725d2e83033c 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 /* function prototypes */
+
 char *
 csv_stringtrim(const char *string, const char *enclosure);
 
@@ -26,3 +27,88 @@ csv_lineparse(const char *stringstart, const char *delimited_by, const char *enc
 
 char *
 csv_stringclean(const char *string, const char *chararray);
+
+void 
+xcsv_data_read(void);
+
+void 
+xcsv_data_write(void);
+
+void 
+xcsv_file_init(void); 
+
+void 
+xcsv_prologue_add(char *);
+
+void 
+xcsv_epilogue_add(char *);
+
+void 
+xcsv_ifield_add(char *, char *, char *);
+
+void 
+xcsv_ofield_add(char *, char *, char *);
+
+void 
+xcsv_destroy_style(void);
+
+/****************************************************************************/
+/* types required for various xcsv functions                                */
+/****************************************************************************/
+
+/* something to map fields to waypts */
+typedef struct field_map {
+       queue Q;
+       char * key;
+       char * val;
+       char * printfc;
+} field_map_t;
+
+/* a queuing struct for prologues / epilogues */
+typedef struct ogue {
+       queue Q;
+       char * val;
+} ogue_t;
+
+/* something to map config file constants to chars */
+typedef struct char_map {
+       const char * key;
+       const char * chars;
+} char_map_t;
+
+/* 
+ * a type describing all the wonderful elements of xcsv files, in a 
+ * nutshell.
+ */
+typedef struct {
+    int is_internal;           /* bool - is internal (1) or parsed (0) */
+
+    int prologue_lines;                /* # of lines to ignore at top of the file */
+    int epilogue_lines;                /* # of lines to ignore at bottom of file */
+
+    /* header lines for writing at the top of the file. */
+    queue prologue;
+
+    /* footer lines for writing at the bottom of the file. */
+    queue epilogue;
+
+    char * field_delimiter;    /* comma, quote, etc... */
+    char * record_delimiter;   /* newline, c/r, etc... */
+
+    char * badchars;           /* characters we never write to output */
+    
+    queue ifield;              /* input field mapping */
+    queue * ofield;                    /* output field mapping */
+    
+    int ifield_ct;             /* actual # of ifields */
+    int ofield_ct;             /* actual # of ofields */
+    
+    FILE * xcsvfp;             /* ptr to current *open* data file */
+    
+} xcsv_file_t;
+
+
+/****************************************************************************/
+/* obligatory global struct                                                 */
+/****************************************************************************/
+xcsv_file_t xcsv_file;
index 264ad482f0ae9279167aa4f83bd535c5c8ca7b8b..d7c6f318636231113849f544e16011129268a1f6 100644 (file)
@@ -212,6 +212,7 @@ void *xcalloc(size_t nmemb, size_t size);
 void *xmalloc(size_t size);
 char *xstrdup(const char *s);
 void rtrim(char *s);
+signed int get_tz_offset(void);
 
 /*
  * PalmOS records like fixed-point numbers, which should be rounded
index db20ccb131f2141034256a449985229daa2dcebc..6a90375b5971489f2c07ea408fddb604a64df562 100644 (file)
 
 #include "defs.h"
 #include "csv_util.h"
-#include <ctype.h>
 
 #define MYNAME "MXF"
 
-static FILE *file_in;
-static FILE *file_out;
-
-static void 
-rd_init(const char *fname, const char *args)
+static void
+mxf_set_style()
 {
-    file_in = fopen(fname, "r");
-    if (file_in == NULL) {
-       fatal(MYNAME ": Cannot open %s for reading\n", fname);
+    /* set up the csv xcsv_file struct */
+    xcsv_file_init();
+
+    /* this is an internal style, don't mess with it */
+    xcsv_file.is_internal = 1;
+
+    /* how the file gets split up */
+    xcsv_file.field_delimiter = ", ";
+    xcsv_file.record_delimiter = "\n";
+    xcsv_file.badchars = "\",";
+
+    xcsv_ifield_add("LAT_DECIMAL", "", "%08.5f");
+    xcsv_ifield_add("LON_DECIMAL", "", "%08.5f");
+    xcsv_ifield_add("DESCRIPTION", "", "%s");
+    xcsv_ifield_add("SHORTNAME", "", "%s");
+    xcsv_ifield_add("IGNORE", "", "%s");
+    xcsv_ifield_add("CONSTANT", "ff0000", "%s");
+    xcsv_ifield_add("CONSTANT", "47", "%s");
+
+    xcsv_ofield_add("LAT_DECIMAL", "", "%08.5f");
+    xcsv_ofield_add("LON_DECIMAL", "", "%08.5f");
+    xcsv_ofield_add("DESCRIPTION", "", "\"%s\"");
+    xcsv_ofield_add("SHORTNAME", "", "\"%s\"");
+    xcsv_ofield_add("DESCRIPTION", "", "\"%s\"");
+    xcsv_ofield_add("CONSTANT", "ff0000", "%s");
+    xcsv_ofield_add("CONSTANT", "47", "%s");
+
+    /* set up mkshort */
+    if (global_opts.synthesize_shortnames) {
+        setshort_length(32);
+        setshort_whitespace_ok(0);
+        setshort_badchars(xcsv_file.badchars);
     }
 }
 
-static void 
-rd_deinit(void)
+static void
+mxf_rd_init(const char *fname, const char *args)
 {
-    fclose(file_in);
-}
+    mxf_set_style();
 
-static void 
-wr_init(const char *fname, const char *args)
-{
-    file_out = fopen(fname, "w");
-    if (file_out == NULL) {
-       fatal(MYNAME ": Cannot open %s for writing\n", fname);
+    xcsv_file.xcsvfp = fopen(fname, "r");
+    
+    if (xcsv_file.xcsvfp == NULL) {
+        fatal(MYNAME ": Cannot open %s for reading\n", fname );
     }
 }
 
-static void 
-wr_deinit(void)
+static void
+mxf_wr_init(const char *fname, const char *args)
 {
-    fclose(file_out);
-}
+    mxf_set_style();
 
-static void 
-data_read(void)
-{
-    char buff[1024];
-    char *s;
-    waypoint *wpt_tmp;
-    int i;
-    int linecount = 0;
-
-    do {
-        linecount++;
-       memset(buff, '\0', sizeof(buff));
-       fgets(buff, sizeof(buff), file_in);
-
-       if (strlen(buff)) {
-
-           wpt_tmp = xcalloc(sizeof(*wpt_tmp), 1);
-
-           /* data delimited by commas, possibly enclosed in quotes.  */
-           s = buff;
-
-           s = csv_lineparse(s, ",", "\"", linecount);
-           
-           i = 0;
-           while (s) {
-               switch (i) {
-               case 0:
-                   wpt_tmp->position.latitude.degrees = atof(s);
-                   break;
-               case 1:
-                   wpt_tmp->position.longitude.degrees = atof(s);
-                   break;
-               case 2:
-                   wpt_tmp->description = csv_stringtrim(s, "");
-                   break;
-               case 3:
-                   wpt_tmp->shortname = csv_stringtrim(s, "");
-                   break;
-               case 4:
-                    /* ignore.  another name-type  */
-                   break;
-               case 5:
-                   /* ignore: color  */
-                   break;
-               case 6:
-                   /* ignore: icon */
-                   break;
-               default:
-                   /* whoa! nelly */
-                   fprintf (stderr, "%s: Warning: data fields on line %d exceed specification.\n", 
-                       MYNAME, linecount);
-                   break;
-               }
-               i++;
-
-               s = csv_lineparse(NULL, ",", "\"", linecount);
-           }
-           
-           if (i != 7) {
-               free(wpt_tmp);
-               fprintf (stderr, "%s: WARNING - extracted %d fields from line %d. \nData on line ignored.\n", 
-                   MYNAME, i, linecount);
-           } else {
-               waypt_add(wpt_tmp);
-           }
-
-       } else {
-            /* empty line */
-       }
-
-    } while (!feof(file_in));
-}
-
-static void 
-mxf_waypt_pr(const waypoint * wpt)
-{
-    int icon = 47; /* default to "dot" */
-    const char *color_hex = "ff0000";
-    char *shortname = NULL;
-    char *description = NULL;
-
-    if ((! wpt->shortname) || (global_opts.synthesize_shortnames)) {
-        if (wpt->description) {
-            if (global_opts.synthesize_shortnames)
-                shortname = mkshort(wpt->description);
-            else
-                shortname = csv_stringclean(wpt->description, ",\"");
-        } else {
-            /* no description available */
-            shortname = xstrdup("");
-        }
-    } else{
-        shortname = csv_stringclean(wpt->shortname, ",\"");
+    xcsv_file.xcsvfp = fopen(fname, "w");
+    
+    if (xcsv_file.xcsvfp == NULL) {
+        fatal(MYNAME ": Cannot open %s for reading\n", fname );
     }
-
-    if (! wpt->description) {
-        if (shortname) {
-            description = csv_stringclean(shortname, ",\"");
-        } else {
-            description = xstrdup("");
-        }
-    } else{
-        description = csv_stringclean(wpt->description, ",\"");
-    }
-
-    fprintf(file_out, "%08.5f, %08.5f, \"%s\", \"%s\", \"%s\", %s, %d\n",
-           wpt->position.latitude.degrees, wpt->position.longitude.degrees,
-           description, shortname, description, 
-           color_hex, icon);
-
-    if (description)
-       free(description);
-    if (shortname)
-       free(shortname);
 }
 
-static void 
-data_write(void)
+static void
+mxf_deinit(void)
 {
-    if (global_opts.synthesize_shortnames) {
-        setshort_length(32);
-        setshort_whitespace_ok(0);
-        setshort_badchars("\",");
-    }
-
-    waypt_disp_all(mxf_waypt_pr);
+    if (xcsv_file.xcsvfp) 
+        fclose(xcsv_file.xcsvfp);
+        
+    xcsv_destroy_style();
 }
 
 ff_vecs_t mxf_vecs = {
-    rd_init,
-    wr_init,
-    rd_deinit,
-    wr_deinit,
-    data_read,
-    data_write,
+    mxf_rd_init,
+    mxf_wr_init,
+    mxf_deinit,
+    mxf_deinit,
+    xcsv_data_read,
+    xcsv_data_write,
 };
-
index b04d05dac16264b1cbc380a813002ab9a81ba440..9d3e8bc5b9e9b5d80b02aec751a457479a98b1f2 100644 (file)
 
 #include "defs.h"
 #include "csv_util.h"
-#include <ctype.h>
-#include <math.h> /* for floor */
 
 #define MYNAME "OZI"
 
-static FILE *file_in;
-static FILE *file_out;
-
-static void 
-rd_init(const char *fname, const char *args)
+static void
+ozi_set_style()
 {
-    file_in = fopen(fname, "r");
-    if (file_in == NULL) {
-       fatal(MYNAME ": Cannot open %s for reading\n", fname);
+    /* set up the ozi xcsv_file struct */
+    xcsv_file_init();
+
+    /* this is an internal style, don't mess with it */
+    xcsv_file.is_internal = 1;
+
+    /* how the file gets split up */
+    xcsv_file.field_delimiter = ",";
+    xcsv_file.record_delimiter =       "\n";
+    xcsv_file.badchars = ",";
+
+    /* prologue */
+    xcsv_prologue_add("OziExplorer Waypoint File Version 1.1");
+    xcsv_prologue_add("WGS 84");
+    xcsv_prologue_add("Reserved 2");
+    xcsv_prologue_add("Reserved 3");
+
+    /* individual field mappings */
+    xcsv_ifield_add("INDEX", "1", "%4d");
+    xcsv_ifield_add("SHORTNAME", "", "%-14.14s");
+    xcsv_ifield_add("LAT_DECIMAL", "", "%11.6f");
+    xcsv_ifield_add("LON_DECIMAL", "", "%11.6f");
+    xcsv_ifield_add("EXCEL_TIME", "", "%011.5f");
+    xcsv_ifield_add("CONSTANT", "0", "%3s");           /* icon */
+    xcsv_ifield_add("CONSTANT", "1", "%2s");           /* 1 */
+    xcsv_ifield_add("CONSTANT", "3", "%2s");           /* display format opts */
+    xcsv_ifield_add("CONSTANT", "0", "%10s");          /* foreground color */
+    xcsv_ifield_add("CONSTANT", "65535", "%10s");      /* background color */
+    xcsv_ifield_add("DESCRIPTION", "", "%-40.40s");    
+    xcsv_ifield_add("CONSTANT", "0", "%2s");           /* pointer direction */
+    xcsv_ifield_add("CONSTANT", "0", "%2s");           /* garmin display flags */
+    xcsv_ifield_add("CONSTANT", "0", "%5s");           /* proximity distance */
+    xcsv_ifield_add("ALT_FEET", "", "%7.0f");
+    xcsv_ifield_add("CONSTANT", "6", "%2s");           /* waypt name text size */
+    xcsv_ifield_add("CONSTANT", "0", "%2s");           /* bold checkbox */
+    xcsv_ifield_add("CONSTANT", "17", "%2s");          /* symbol size */
+
+    /* outfields are infields */
+    if (xcsv_file.ofield)
+        free(xcsv_file.ofield);
+    xcsv_file.ofield = &xcsv_file.ifield;
+    xcsv_file.ofield_ct = xcsv_file.ifield_ct;
+
+    /* set up mkshort */
+    if (global_opts.synthesize_shortnames) {
+        setshort_length(32);
+        setshort_whitespace_ok(0);
+        setshort_badchars(xcsv_file.badchars);
     }
 }
 
-static void 
-rd_deinit(void)
-{
-    fclose(file_in);
-}
-
-static void 
-wr_init(const char *fname, const char *args)
+static void
+ozi_rd_init(const char *fname, const char *args)
 {
-    file_out = fopen(fname, "w");
-    if (file_out == NULL) {
-       fatal(MYNAME ": Cannot open %s for writing\n", fname);
+    ozi_set_style();
+    
+    xcsv_file.xcsvfp = fopen(fname, "r");
+    
+    if (xcsv_file.xcsvfp == NULL) {
+        fatal(MYNAME ": Cannot open %s for reading\n", fname);
     }
 }
 
-static void 
-wr_deinit(void)
-{
-    fclose(file_out);
-}
-
-static void 
-data_read(void)
-{
-    char buff[1024];
-    char *s;
-    waypoint *wpt_tmp;
-    int i;
-    int linecount = 0;
-    double alt;
-
-    do {
-        linecount++;
-       memset(buff, '\0', sizeof(buff));
-       fgets(buff, sizeof(buff), file_in);
-
-       if ((strlen(buff)) && (strstr(buff, ",") != NULL)) {
-
-           wpt_tmp = xcalloc(sizeof(*wpt_tmp), 1);
-
-           /* data delimited by commas, possibly enclosed in quotes.  */
-           s = buff;
-           s = csv_lineparse(s, ",", "", linecount);
-
-           i = 0;
-           while (s) {
-               switch (i) {
-               case 0:
-                   /* sequence # */
-                   break;
-               case 1:
-                   /* waypoint name */
-                   wpt_tmp->shortname = csv_stringtrim(s, "");
-                   break;
-               case 2:
-                   /* degrees latitude */
-                   wpt_tmp->position.latitude.degrees = atof(s);
-                   break;
-               case 3:
-                   /* degrees longitude */
-                   wpt_tmp->position.longitude.degrees = atof(s);
-                   break;
-               case 4:
-                    /* DAYS since 1900 00:00:00 in days.days (5.5) */
-                   wpt_tmp->creation_time = (atof(s) - 25569.0) * 86400.0;                    
-                   break;
-               case 5:
-                   /* icons 0-xx */
-                   break;
-               case 6:
-                   /* unknown - always 1 */
-                   break;
-               case 7:
-                   /* display format options 0-8 */
-                   break;
-               case 8:
-                   /* foreground color (0=black) */
-                   break;
-               case 9:
-                   /* background color (65535=yellow)*/
-                   break;
-               case 10:
-                   /* Description */
-                   wpt_tmp->description = csv_stringtrim(s, "");
-
-                   break;
-               case 11:
-                   /* pointer direction 0,1,2,3 bottom,top,left,right */
-                   break;
-               case 12:
-                   /* garmin gps display flags (0-name w/sym, 1-sym only, 2-comment w/symbol */
-                   break;
-               case 13:
-                   /* proximity distance - meters */
-                   break;
-               case 14:
-                   /* altitude in feet */
-                   alt = atof(s);
-                   if (alt == -777) {
-                       wpt_tmp->position.altitude.altitude_meters = unknown_alt;
-                   } else {
-                       wpt_tmp->position.altitude.altitude_meters = alt * .3048;
-                   }
-                   break;
-               case 15:
-                   /* waypoint text name size */
-                   break;
-               case 16:
-                   /* bold checkbox (1=bold, default 0) */
-                   break;
-               case 17:
-                   /* symbol size - 17 default */
-                   break;
-                   /* 
-                    * Fields 18-23 were added around version 3.90.4g of
-                    * Ozi, but aren't documented.   We silently ignore 
-                    * these or any additional fields we don't need.
-                    */
-               default:
-                   break;
-               }
-               i++;
-
-               s = csv_lineparse(NULL, ",", "", linecount);
-           }
-           
-           waypt_add(wpt_tmp);
-
-       } else {
-            /* empty line */
-       }
-
-    } while (!feof(file_in));
-}
-
-static void 
-ozi_waypt_pr(const waypoint * wpt)
+static void
+ozi_wr_init(const char *fname, const char *args)
 {
-    static int index = 0;
-    double alt_feet;
-    double ozi_time;
-    char * description;
-    char * shortname;
+    ozi_set_style();
 
-    ozi_time = (wpt->creation_time / 86400.0) + 25569.0;
-    if (wpt->position.altitude.altitude_meters == unknown_alt) {
-       alt_feet = -777;
-    } else {
-       alt_feet = (wpt->position.altitude.altitude_meters * 3.2808); 
-    }
+    xcsv_file.xcsvfp = fopen(fname, "w");
     
-    if ((! wpt->shortname) || (global_opts.synthesize_shortnames)) {
-        if (wpt->description) {
-            if (global_opts.synthesize_shortnames)
-                shortname = mkshort(wpt->description);
-            else
-                shortname = csv_stringclean(wpt->description, ",");
-        } else {
-            /* no description available */
-            shortname = xstrdup("");
-        }
-    } else{
-        shortname = csv_stringclean(wpt->shortname, ",");
-    }
-
-    if (! wpt->description) {
-        if (shortname) {
-            description = csv_stringclean(shortname, ",");
-        } else {
-            description = xstrdup("");
-        }
-    } else{
-        description = csv_stringclean(wpt->description, ",");
+    if (xcsv_file.xcsvfp == NULL) {
+        fatal(MYNAME ": Cannot open %s for reading\n", fname);
     }
-
-    index++;
-
-    fprintf(file_out, "%4d,%-14.14s,%11.6f,%11.6f,%011.5f,%3d,%2d,%2d,%10d,%10d,%-40.40s,%2d,%2d,%5d,%7.0f,%2d,%2d,%2d\n", 
-       index, shortname, wpt->position.latitude.degrees, 
-       wpt->position.longitude.degrees, ozi_time, 0, 1, 3, 0, 65535,
-       description, 0, 0, 0, alt_feet, 6, 0, 17);
-
-    free(description);
-    free(shortname);
-
 }
 
-static void 
-data_write(void)
+static void
+ozi_deinit(void)
 {
 
-    fprintf(file_out, "OziExplorer Waypoint File Version 1.1\n");
-    fprintf(file_out, "WGS 84\n");
-    fprintf(file_out, "Reserved 2\n");
-    fprintf(file_out, "Reserved 3\n");
-
-    if (global_opts.synthesize_shortnames) {
-        setshort_length(32);
-        setshort_whitespace_ok(0);
-        setshort_badchars("\",");
-    }
-
-    waypt_disp_all(ozi_waypt_pr);
+    if (xcsv_file.xcsvfp) 
+        fclose(xcsv_file.xcsvfp);
+        
+        xcsv_destroy_style();
 }
 
 ff_vecs_t ozi_vecs = {
-    rd_init,
-    wr_init,
-    rd_deinit,
-    wr_deinit,
-    data_read,
-    data_write,
+    ozi_rd_init,
+    ozi_wr_init,
+    ozi_deinit,
+    ozi_deinit,
+    xcsv_data_read,
+    xcsv_data_write,
 };
-
diff --git a/gpsbabel/reference/xmapwpt.wpt b/gpsbabel/reference/xmapwpt.wpt
new file mode 100644 (file)
index 0000000..48885aa
--- /dev/null
@@ -0,0 +1,9 @@
+1296126539:1481466224:1845728360:1416544806:3137157:GCEBB::Mountain Bike Heaven by susy1313
+1296126539:1481466224:1844733052:1420362881:3137157:GC1A37::The Troll by a182pilot & Family
+1296126539:1481466224:1845525076:1420861444:3137157:GC1C2B::Dive Bomber by JoGPS & family
+1296126539:1481466224:1845170937:1420622369:3137157:GC25A9::FOSTER by JoGPS & Family
+1296126539:1481466224:1844552696:1419840970:3137157:GC2723::Logan Lighthouse by JoGps & Family
+1296126539:1481466224:1844956189:1419432025:3137157:GC2B71::Ganier Cache by Susy1313
+1296126539:1481466224:1844757518:1419270824:3137157:GC309F::Shy's Hill by FireFighterEng33
+1296126539:1481466224:1845011414:1418580721:3137157:GC317A::GittyUp by JoGPS / Warner Parks
+1296126539:1481466224:1844799182:1418788060:3137157:GC317D::Inlighting by JoGPS / Warner Parks
diff --git a/gpsbabel/style/README.style b/gpsbabel/style/README.style
new file mode 100644 (file)
index 0000000..bb56f4c
--- /dev/null
@@ -0,0 +1,254 @@
+gpsbabel XCSV Style File Layout:
+
+The format of an XCSV style file is quite simple and designed to be easily
+implemented by non-programmers to handle "one-off" babel-ization of various
+XCSV (whatever separated values) text files.  The format and usage of the 
+various style directives are described below.
+
+The first and foremost important step is understanding how the config
+file is laid out itself.  The format is:
+
+DIRECTIVE<whitespace>VALUE
+
+Where <whitespace> is a space, tab, spaces, tabs, etc...  There should
+be *nothing* before the directive.  (i.e. not "  DIRECTIVE  VALUE")
+
+INTERNAL CONSTANTS:
+A few internal constants are defined in the XCSV parser to make the style
+file simpler.  They may or may be used and are optional in most cases. 
+Note that only certain style file directives map these constants.
+
+STYLE CONSTANT  MAPS TO CHAR(s)
+---------------------------------------
+COMMA          ,
+COMMASPACE     ,<space> 
+SINGLEQUOTE    '
+DOUBLEQUOTE    "
+COLON          :
+SEMICOLON      ;
+NEWLINE                \n
+CR             \r
+CRNEWLINE      \r\n
+TAB            \t
+SPACE                  <space>
+HASH           #
+
+COMMENTS:
+Anything after a hash (#) on a line is not parsed.  For example:
+#THIS ENTIRE LINE IS A COMMENT.
+#FIELD LAT_DECIMAL, "", "%lf"   THIS ENTIRE LINE IS A COMMENT
+FIELD LAT_DECIMAL, "", "%lf"  # ONLY THIS SENTENCE IS A COMMENT.
+
+
+DEFINING THE LAYOUT OF THE FILE:
+--------------------------------
+The first few directives define the layout the physical file itself:
+
+ o FIELD_DELIMITER:
+   The field delimiter defines the character(s) that separate the fields in 
+   the rows of data inside the XCSV file.  Common field delimiters are commas 
+   and tabs. (referred to as "comma separated values" and "tab separated 
+   values")
+
+   examples: FIELD_DELIMITER   COMMA
+             FIELD_DELIMITER   ~
+
+   The directive FIELD_DELIMITER is parsed for STYLE CONSTANTS as defined in 
+   the table above.
+
+ o RECORD DELIMITER:
+   The record delimiter defines that character(s) that separate ROWS of 
+   data (FIELDS) in the XCSV file.  The most common record delimiters
+   are NEWLINE and CR (carriage return).
+
+   example: RECORD_DELIMITER   NEWLINE
+            RECORD_DELIMITER    |
+
+   The directive RECORD_DELIMITER is parsed for STYLE CONSTANTS as defined 
+   in the table above.
+
+ o BADCHARS:
+   Bad characters are things that should *never* be written into the XCSV
+   file on output.  Common bad characters are usually the FIELD_DELIMITER
+   itself.
+
+   example: BADCHARS   COMMA
+            BADCHARS   ~|
+
+   The directive BADCHARS is parsed for STYLE CONSTANTS as defined in the 
+   table above.
+
+ o PROLOGUE
+   A prologue is basically constant data that is written to the output
+   file BEFORE any waypoints are processed.  PROLOGUE can be defined 
+   multiple times in the style file, once for each "line" before the data 
+   begins.  This is commonly used in XCSV files as a "header" row.  
+
+   example: PROLOGUE   OziExplorer Waypoint File Version 1.1
+            PROLOGUE   WGS 84
+   * or *
+            PROLOGUE   Symbol,Name,Latitude,Longitude
+
+ o EPILOGUE
+   An Epilogue is the same as a prologue, except this data is written at 
+   the END of the file.  See the examples for PROLOGUE above.
+
+
+DEFINING FIELDS WITHIN THE FILE:
+-------------------------------
+
+A field defines data.  There are two different classifications of FIELDS,
+IFIELD (file input) and OFIELD (file output).  In the absence of any OFIELDS,
+IFIELDS are use as both input and output.  The existence of OFIELDS is 
+primarily to allow more flexible mapping of gpsbabel data to output data 
+(say, for instance, to map the internal gpsbabel "description" variable to 
+two or more fields on output).  For all practical purposes, IFIELDS and 
+OFIELDS are defined the same way in the style file.
+
+There are several different types of fields that may be defined.  Each field 
+consists of three pieces of information: the FIELD TYPE, a DEFAULT VALUE, and 
+a PRINTF CONVERSION (for output).  In many cases, not all pieces are used,
+but all 3 pieces are required.
+
+FIELDS should be defined in the style file in the logical order that they
+appear in the data, from left to right.  This is the order in which they are
+parsed from input and written to output.
+
+The fields used by the XCSV parser are as follows:
+
+ o IGNORE 
+   IGNORE fields are, guess what, ignored on input.   Internally, IGNORE 
+   fields are treated as CHARACTER data, and as such, require a printf 
+   conversion for a character array.  
+
+   example: IFIELD IGNORE,"","%14.14s"    (writes a 14 character blank field)
+            IFIELD IGNORE,"","%s"         (writes a blank field on output)
+
+ o CONSTANT 
+   CONSTANT fields are, of course, constant.  They are ignored on input, 
+   however they write CONSTANT data on output.  As such, they require a 
+   DEFAULT VALUE and a printf conversion for a character array.
+  
+   example: IFIELD CONSTANT,"FFFFFF","%s"   (writes "FFFFFF" in the field)
+            IFIELD CONSTANT,"01/01/70","%s" (a constant date field)
+
+ o INDEX
+   An INDEX field is used ONLY on output.  The INDEX constant defines a field 
+   that, at output, contains the sequence number of the waypoint being 
+   written, starting at 0.  An index is managed internally as an INTEGER 
+   and requires an INTEGER printf conversion.  An INDEX has one special
+   property.  The DEFAULT VALUE of the index is added to the index 
+   on each iteration (to allow indexes starting at 1, 100, etc..).   
+
+   example: IFIELD INDEX,"0","%04d"      (Starts counting at zero)
+            IFIELD INDEX,"","%04d"       (Starts counting at zero)
+            IFIELD INDEX,"1","%04d"      (Starts counting at one)
+
+ o SHORTNAME
+   A SHORTNAME is generally the waypoint name of the data being processed.  
+   SHORTNAME maps directly to the gpsbabel variable ->shortname.  A SHORTNAME 
+   is CHARACTER data and requires a character array printf conversion.
+
+   example: IFIELD SHORTNAME,"","%s"    (write shortname in the output file)
+
+ o DESCRIPTION
+   A DESCRIPTION is generally a long description of the waypoint.  A 
+   DESCRIPTION maps to the gpsbabel variable ->description and is otherwise 
+   handled exactly like a SHORTNAME.
+
+   example: IFIELD DESCRIPTION,"","%s"  (write description in the output file)
+
+ o NOTES
+   NOTES are generally everything else about a waypoints. NOTES map to the 
+   gpsbabel variable ->notes and is otherwise handled exactly like a 
+   SHORTNAME.
+
+ o URL
+   URL is a URL for the waypoint.  URL maps to the gpsbabel variable 
+   ->url and is otherwise handled exactly like a SHORTNAME.
+
+   example: IFIELD URL,"","%s"         (writes the URL in the output file)
+
+ o URL_LINK_TEXT
+   URL_LINK_TEXT is a textual description of where a URL points.  
+   URL_LINK_TEXT maps to the gpsbabel variable ->url_link_text and 
+   is otherwise handled exactly like a SHORTNAME.
+
+   example: IFIELD URL_LINK_TEXT,"","%s" (writes link text in the output file)
+
+ o ICON_DESCR
+   ICON_DESCR is a textual description of an icon type for a waypoint.
+   ICON_DESCR maps to the gpsbabel variable ->icon_desc and is otherwise 
+   handled exactly like a SHORTNAME.
+
+   example: IFIELD ICON_DESCR,"","%s" (writes link text in the output file)
+
+ o LAT_DECIMAL
+   LAT_DECIMAL defines LATITUDE in DECIMAL format.  Note that this is a PURE 
+   signed decimal format (i.e. -91.0000).  This data is handled internally as 
+   a DOUBLE PRECISION FLOAT and requires a FLOATING POINT printf conversion.
+
+   example: IFIELD LAT_DECIMAL,"","%lf"
+
+ o LON_DECIMAL
+   See LAT_DECIMAL, except LON_DECIMAL defines LONGITUDE.
+
+ o LAT_INT32DEG
+   LAT_INT32DEG defines LATITUDE in what I call INT32DEGREES.  This value is 
+   a signed LONG INTEGER and requires a LONG INTEGER printf conversion. 
+
+   example: IFIELD LAT_INT32DEG,"","%ld"
+
+ o LON_INT32DEG
+   See LON_INT32DEG except LON_INT32DEG defines LONGITUDE.
+
+ o LAT_DECIMALDIR / LAT_DIRDECIMAL
+   LAT_DECIMALDIR and LAT_DIRDECIMAL  defines LATITUDE in DECIMAL format 
+   with the added bonus of a 'N/S' or 'E/W' direction character.  This data 
+   is handled internally as a DOUBLE PRECISION FLOAT and a single 
+   CHARACTER and requires a FLOATING POINT as well as a CHARACTER printf 
+   conversion.  The only difference between the two is whether the directional
+   character appears before (LAT_DIRDECIMAL) or after (LAT_DECIMALDIR) the
+   decimal number.
+   
+   example: IFIELD LAT_DECIMALDIR,"","%lf %c"
+   example: IFIELD LAT_DIRDECIMAL,"","%c %08.5lf"
+
+ o LON_DECIMALDIR / LON_DIRDECIMAL
+   Same as LAT_DECIMALDIR / LAT_DIRDECIMAL except LON_ defines LONGITUDE.
+
+
+ o ALT_FEET
+   ALT_FEET is the position's ALTITUDE in FEET.  This value is treated as 
+   a SIGNED DOUBLE PRECISION FLOAT and requires a FLOATING POINT printf 
+   conversion.
+
+   example: IFIELD ALT_FEET,"","%.0lf"
+
+ o ALT_METERS
+   ALT_METERS is identical to ALT_FEET with the exception that the altitude
+   is in METERS.
+
+ o EXCEL_TIME
+   EXCEL_TIME is the waypoint's creation time, if any.  This is actually
+   the decimal days since 1/1/1900 and is handled internally as a DOUBLE
+   PRECISION FLOAT and requires a FLOATING POINT printf conversion.
+
+   example: IFIELD EXCEL_TIME,"","%11.5lf"
+
+ o TIMET_TIME
+   TIMET_TIME is the waypoint's creation time, if any.  This is actually
+   the integer seconds since 1/1/1970 (let's not start the holy war) and
+   is handled internally as a LONG INTEGER and requires a LONG INTEGER
+   printf conversion.
+   
+   example: IFIELD TIMET_TIME,"","%ld"
+
+
+EXAMPLES:
+--------
+For examples on using the XCSV module, please see the *.style files in
+the style/ subdirectory of gpsbabel.  For examples of using the XCSV
+module instead of carving out trivial C code, see the source code 
+examples ozi.c, mxf.c, and xmapwpt.c in the gpsbabel directory.
+
diff --git a/gpsbabel/style/csv.style b/gpsbabel/style/csv.style
new file mode 100644 (file)
index 0000000..8866a73
--- /dev/null
@@ -0,0 +1,22 @@
+# gpsbabel XCSV style file
+#
+# Format: Delorme SA 9.0 CSV
+# Author: Alex Mottram
+#   Date: 12/09/2002
+#
+# 
+# As defined in csv.c
+#
+#
+# FILE LAYOUT DEFINITIIONS:
+#
+FIELD_DELIMITER                COMMASPACE
+RECORD_DELIMITER       NEWLINE
+BADCHARS               COMMA
+
+#
+# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE:
+#
+IFIELD LAT_DECIMAL, "", "%08.5lf"
+IFIELD LON_DECIMAL, "", "%08.5lf"
+IFIELD DESCRIPTION, "", "%s"
diff --git a/gpsbabel/style/custom.style b/gpsbabel/style/custom.style
new file mode 100644 (file)
index 0000000..af22154
--- /dev/null
@@ -0,0 +1,48 @@
+# gpsbabel XCSV style file
+#
+# Format: Custom "Everything" Style
+# Author: Alex Mottram
+#   Date: 11/24/2002
+#
+#
+# FILE LAYOUT DEFINITIIONS:
+#
+FIELD_DELIMITER                COMMA
+RECORD_DELIMITER       NEWLINE
+BADCHARS               COMMA
+
+#
+# HEADER STUFF:
+#
+PROLOGUE       Prologue Line 1
+PROLOGUE       Prologue Line 2
+
+#
+# INDIVIDUAL DATA FIELDS:
+#
+IFIELD CONSTANT, "", "CONSTANT"
+IFIELD INDEX, "", "%d"
+IFIELD LAT_DECIMAL, "", "%lf"
+IFIELD LAT_DIR, "", "%c"
+IFIELD LON_DECIMAL, "", "%lf"
+IFIELD LON_DIR, "", "%c"
+IFIELD ICON_DESCR, "", "%s"
+IFIELD SHORTNAME, "", "%s"
+IFIELD DESCRIPTION, "", "%s"
+IFIELD NOTES, "", "%s"
+IFIELD URL, "", "%s"           
+IFIELD URL_LINK_TEXT, "", "%s"
+IFIELD ALT_METERS, "", "%lfM"
+IFIELD ALT_FEET, "", "%lfF"
+IFIELD LAT_DECIMALDIR, "", "%lf/%c"
+IFIELD LON_DECIMALDIR, "", "%lf/%c"
+IFIELD LAT_DIRDECIMAL, "", "%c/%lf"
+IFIELD LON_DIRDECIMAL, "", "%c/%lf"
+IFIELD LAT_INT32DEG, "", "%ld"
+IFIELD LON_INT32DEG, "", "%ld"
+IFIELD TIMET_TIME, "", "%ld"
+IFIELD EXCEL_TIME, "", "%lf"
+
+# EPILOGUE: 
+EPILOGUE       Epilogue Line 1
+EPILOGUE       Epilogue Line 2
diff --git a/gpsbabel/style/dna.style b/gpsbabel/style/dna.style
new file mode 100644 (file)
index 0000000..d94dc39
--- /dev/null
@@ -0,0 +1,24 @@
+# gpsbabel XCSV style file
+#
+# Format: DNA Marker Format
+# Author: Alex Mottram
+#   Date: 12/09/2002
+#
+# 
+# As defined in dna.c
+#
+#
+# FILE LAYOUT DEFINITIIONS:
+#
+FIELD_DELIMITER                COMMA
+RECORD_DELIMITER       NEWLINE
+BADCHARS               COMMA
+
+#
+# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE:
+#
+IFIELD INDEX, "", "%d"
+IFIELD LAT_DECIMAL, "", "%08.5lf"
+IFIELD LON_DECIMAL, "", "%08.5lf"
+IFIELD DESCRIPTION, "", "%s"
+
diff --git a/gpsbabel/style/gpsdrive.style b/gpsbabel/style/gpsdrive.style
new file mode 100644 (file)
index 0000000..e743bcd
--- /dev/null
@@ -0,0 +1,20 @@
+# gpsbabel XCSV style file
+#
+# Format: GPSDrive
+# Author: Alex Mottram
+#   Date: 12/11/2002
+#
+# 
+#
+# FILE LAYOUT DEFINITIIONS:
+#
+FIELD_DELIMITER                SPACE
+RECORD_DELIMITER       NEWLINE
+BADCHARS               ,"
+
+#
+# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE:
+
+IFIELD ANYNAME, "", "%s"
+IFIELD LAT_DECIMAL, "", "%08.5lf"
+IFIELD LON_DECIMAL, "", "%08.5lf"
diff --git a/gpsbabel/style/gpsman.style b/gpsbabel/style/gpsman.style
new file mode 100644 (file)
index 0000000..e6b74c8
--- /dev/null
@@ -0,0 +1,29 @@
+# gpsbabel XCSV style file
+#
+# Format: GPSMAN Format
+# Author: Alex Mottram
+#   Date: 12/09/2002
+#
+# 
+# As defined in gpsman.c
+#
+#
+# FILE LAYOUT DEFINITIIONS:
+#
+FIELD_DELIMITER                TAB
+RECORD_DELIMITER       NEWLINE
+BADCHARS               TAB
+
+PROLOGUE       !Format: DDD 1 WGS 84
+PROLOGUE       !W:
+
+#
+# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE:
+#
+IFIELD SHORTNAME, "", "%-8.8s"
+IFIELD DESCRIPTION, "", "%s"
+IFIELD LAT_DIRDECIMAL, "", "%c%lf"
+IFIELD LON_DIRDECIMAL, "", "%c%lf"
+IFIELD IGNORE, "", "%s"
+
+# gpsman.c likes mkshort len = 8, whitespace = 0.
diff --git a/gpsbabel/style/mxf.style b/gpsbabel/style/mxf.style
new file mode 100644 (file)
index 0000000..4d00c9a
--- /dev/null
@@ -0,0 +1,34 @@
+# gpsbabel XCSV style file
+#
+# Format: Ozi Explorer
+# Author: Alex Mottram
+#   Date: 12/09/2002
+#
+# 
+# As used in mxf.c
+#
+#
+# FILE LAYOUT DEFINITIIONS:
+#
+FIELD_DELIMITER                COMMASPACE
+RECORD_DELIMITER       NEWLINE
+BADCHARS               COMMA
+
+#
+# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE:
+#
+IFIELD LAT_DECIMAL, "", "%08.5f"
+IFIELD LON_DECIMAL, "", "%08.5f"
+IFIELD DESCRIPTION, "", "%s"
+IFIELD SHORTNAME, "", "%s"
+IFIELD IGNORE, "", "%s"
+IFIELD CONSTANT, "ff0000", "%s"        # COLOR
+IFIELD CONSTANT, "47", "%s"            # ICON
+
+OFIELD LAT_DECIMAL, "", "%08.5f"
+OFIELD LON_DECIMAL, "", "%08.5f"
+OFIELD DESCRIPTION, "", ""%s""
+OFIELD SHORTNAME, "", "%s"
+OFIELD DESCRIPTION, "", "%s"
+OFIELD CONSTANT, "ff0000", "%s"        # COLOR
+OFIELD CONSTANT, "47", "%s"            # ICON
diff --git a/gpsbabel/style/nima.style b/gpsbabel/style/nima.style
new file mode 100644 (file)
index 0000000..a9b6845
--- /dev/null
@@ -0,0 +1,42 @@
+# gpsbabel XCSV style file
+#
+# Format: NIMA/GNIS Geographic Names File
+# Author: Alex Mottram
+#   Date: 11/24/2002
+#
+#
+# FILE LAYOUT DEFINITIIONS:
+#
+FIELD_DELIMITER                TAB
+RECORD_DELIMITER       NEWLINE
+BADCHARS               TAB
+PROLOGUE       RC      UFI     UNI     DD_LAT  DD_LONG DMS_LAT DMS_LONG        UTM     JOG     FC      DSG     PC      CC1     ADM1    ADM2    DIM     CC2     NT      LC      SHORT_FORM      GENERIC SORT_NAME       FULL_NAME       FULL_NAME_ND    MODIFY_DATE
+
+#
+# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE:
+#
+IFIELD IGNORE, "", "%s"                # RC
+IFIELD IGNORE, "", "%s"                # UFI
+IFIELD IGNORE, "", "%s"                # UNI
+IFIELD LAT_DECIMAL, "", "%lf"          # DD_LAT
+IFIELD LON_DECIMAL, "", "%lf"          # DD_LON
+IFIELD IGNORE, "", "%s"                # DMS_LAT
+IFIELD IGNORE, "", "%s"                # DMS_LON
+IFIELD IGNORE, "", "%s"                # UTM
+IFIELD IGNORE, "", "%s"                # JOG
+IFIELD IGNORE, "", "%s"                # FC
+IFIELD IGNORE, "", "%s"                # DSG
+IFIELD IGNORE, "", "%s"                # PC
+IFIELD IGNORE, "", "%s"                # CC1
+IFIELD IGNORE, "", "%s"                # ADM1
+IFIELD IGNORE, "", "%s"                # ADM2
+IFIELD IGNORE, "", "%s"                # DIM
+IFIELD IGNORE, "", "%s"                # CC2
+IFIELD IGNORE, "", "%s"                # NT
+IFIELD IGNORE, "", "%s"                # LC
+IFIELD IGNORE, "", "%s"                # SHORT_FORM
+IFIELD IGNORE, "", "%s"                # GENERIC
+IFIELD SHORTNAME, "", "%s"             # SORT_NAME 
+IFIELD IGNORE, "", "%s"                # FULL_NAME (unicoded!)
+IFIELD DESCRIPTION, "", "%s"           # FULL_NAME_ND
+IFIELD IGNORE, "", "%s"                # MODIFY_DATE
diff --git a/gpsbabel/style/ozi.style b/gpsbabel/style/ozi.style
new file mode 100644 (file)
index 0000000..0acea28
--- /dev/null
@@ -0,0 +1,42 @@
+# gpsbabel XCSV style file
+#
+# Format: Ozi Explorer
+# Author: Alex Mottram
+#   Date: 12/09/2002
+#
+# 
+# As used in ozi.c
+#
+#
+# FILE LAYOUT DEFINITIIONS:
+#
+FIELD_DELIMITER                COMMA
+RECORD_DELIMITER       NEWLINE
+BADCHARS               COMMA
+
+PROLOGUE       OziExplorer Waypoint File Version 1.1
+PROLOGUE       WGS 84
+PROLOGUE       Reserved 2
+PROLOGUE       Reserved 3
+
+#
+# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE:
+#
+IFIELD INDEX, "1", "%4d"
+IFIELD SHORTNAME, "", "%-14.14s"
+IFIELD LAT_DECIMAL, "", "%11.6lf"
+IFIELD LON_DECIMAL, "", "%11.6lf"
+IFIELD EXCEL_TIME, "", "%011.5lf"
+IFIELD CONSTANT, "0", "%3s"            # icon 
+IFIELD CONSTANT, "1", "%2s"            # 1 
+IFIELD CONSTANT, "3", "%2s"            # display format opts 
+IFIELD CONSTANT, "0", "%10s"           # foreground color 
+IFIELD CONSTANT, "65535", "%10s"       # background color 
+IFIELD DESCRIPTION, "", "%-40.40s"
+IFIELD CONSTANT, "0", "%2s"            # pointer direction 
+IFIELD CONSTANT, "0", "%2s"            # garmin display flags 
+IFIELD CONSTANT, "0", "%5s"            # proximity distance 
+IFIELD ALT_FEET, "", "%7.0lf"
+IFIELD CONSTANT, "6", "%2s"            # waypt name text size 
+IFIELD CONSTANT, "0", "%2s"            # bold checkbox 
+IFIELD CONSTANT, "17", "%2s"           # symbol size 
diff --git a/gpsbabel/style/s_and_t.style b/gpsbabel/style/s_and_t.style
new file mode 100644 (file)
index 0000000..11a418d
--- /dev/null
@@ -0,0 +1,33 @@
+# gpsbabel XCSV style file
+#
+# Format: MS S&T 2002/2003
+# Author: Alex Mottram
+#   Date: 12/09/2002
+#
+# 
+# As requested by Noel Shrum on the gpsbabel-code mailing list.
+# Name,Latitude,Longitude,Name 2,URL,Type
+# GCCBF,44.479133,-85.56515,High Rollaway by rjlint,http://www.geocaching.com/seek/cache_details.aspx?ID=3263,Traditional Cache
+# GC110D,44.6522,-85.492483,Brown Bridge Pond Peek-a-Boo Cache by Big Bird,http://www.geocaching.com/seek/cache_details.aspx?ID=4365,Traditional Cache
+# GC171C,44.70605,-85.62265,The Michigan Frog by RealDcoy & LRB,http://www.geocaching.com/seek/cache_details.aspx?ID=5916,Traditional Cache
+#
+#
+# FILE LAYOUT DEFINITIIONS:
+#
+FIELD_DELIMITER                COMMA
+RECORD_DELIMITER       NEWLINE
+BADCHARS               COMMA
+
+PROLOGUE       Name,Latitude,Longitude,Name 2,URL,Type
+
+#
+# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE:
+# NOTE: MS S&T ONLY IMPORTS DATA, IT DOESN'T EXPORT THIS ANYWHERE SO WE CAN
+#       HAVE OUR WAY WITH THE FORMATTING. 
+#
+IFIELD SHORTNAME, "", "%s"             # Name
+IFIELD LAT_DECIMAL, "", "%lf"          # Latitude
+IFIELD LON_DECIMAL, "", "%lf"          # Longitude
+IFIELD DESCRIPTION, "", "%s"           # Name 2 (Big Description)
+IFIELD URL, "", "%s"                   # URL
+IFIELD IGNORE, "", ""                  # Holder for Geocache Type
diff --git a/gpsbabel/style/tiger.style b/gpsbabel/style/tiger.style
new file mode 100644 (file)
index 0000000..8254ea9
--- /dev/null
@@ -0,0 +1,23 @@
+# gpsbabel XCSV style file
+#
+# Format: Tiger Data Format
+# Author: Alex Mottram
+#   Date: 12/09/2002
+#
+# 
+# As defined in tiger.c
+#
+#
+# FILE LAYOUT DEFINITIIONS:
+#
+FIELD_DELIMITER                COLON
+RECORD_DELIMITER       NEWLINE
+BADCHARS               COLON
+
+#
+# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE:
+#
+IFIELD LON_DECIMAL, "", "%lf"
+IFIELD LAT_DECIMAL, "", "%lf"
+IFIELD CONSTANT, "redpin", "%s"
+IFIELD DESCRIPTION, "", "%s"
diff --git a/gpsbabel/style/xmap.style b/gpsbabel/style/xmap.style
new file mode 100644 (file)
index 0000000..7570667
--- /dev/null
@@ -0,0 +1,24 @@
+# gpsbabel XCSV style file
+#
+# Format: Delorme Xmap Conduit
+# Author: Alex Mottram
+#   Date: 12/09/2002
+#
+# 
+# As defined in csv.c/xmap
+#
+#
+# FILE LAYOUT DEFINITIIONS:
+#
+FIELD_DELIMITER                COMMASPACE
+RECORD_DELIMITER       NEWLINE
+BADCHARS               COMMA
+
+PROLOGUE       BEGIN SYMBOL
+EPILOGUE       END
+#
+# INDIVIDUAL DATA FIELDS, IN ORDER OF APPEARANCE:
+#
+IFIELD LAT_DECIMAL, "", "%08.5lf"
+IFIELD LON_DECIMAL, "", "%08.5lf"
+IFIELD DESCRIPTION, "", "%s"
index 57145ed893c5c807c64b4abedd2715c78b7ed8f0..577ab6080e645cdc8c95a9987577b90092f6214f 100755 (executable)
@@ -166,3 +166,24 @@ diff ${TMPDIR}/gpsdrive.txt reference
 ${PNAME} -i gpsdrive -f reference/gpsdrive.txt -o gpsdrive -F ${TMPDIR}/gpsdrive2.txt
 diff ${TMPDIR}/gpsdrive2.txt reference/gpsdrive.txt
 
+# XMapHH Street Atlas USA file format
+rm -f ${TMPDIR}/xmapwpt.wpt ${TMPDIR}/xmapwpt.xmapwpt
+${PNAME} -i xmapwpt -f reference/xmapwpt.wpt -o xmapwpt -F ${TMPDIR}/xmapwpt.xmapwpt
+${PNAME} -i xmapwpt -f ${TMPDIR}/xmapwpt.xmapwpt -o xmapwpt -F ${TMPDIR}/xmapwpt.wpt
+diff ${TMPDIR}/xmapwpt.wpt reference
+
+# XCSV
+# Test that we can parse a style file, and read and write data in the 
+# same xcsv format (a complete test is virtually impossible).
+echo "RECORD_DELIMITER NEWLINE" > ${TMPDIR}/testo.style
+echo "FIELD_DELIMITER COMMA" >> ${TMPDIR}/testo.style
+echo "BADCHARS COMMA" >> ${TMPDIR}/testo.style
+echo "PROLOGUE Header" >> ${TMPDIR}/testo.style
+echo "EPILOGUE Footer" >> ${TMPDIR}/testo.style
+echo "IFIELD SHORTNAME,,%s" >> ${TMPDIR}/testo.style
+echo "IFIELD LAT_DIRDECIMAL,,%c%lf" >> ${TMPDIR}/testo.style
+echo "IFIELD LON_DECIMALDIR,,%lf%c" >> ${TMPDIR}/testo.style
+rm -f ${TMPDIR}/xcsv.geo ${TMPDIR}/xcsv.xcsv
+${PNAME} -i geo -f geocaching.loc -o xcsv,style=${TMPDIR}/testo.style -F ${TMPDIR}/xcsv.geo
+${PNAME} -i xcsv,style=${TMPDIR}/testo.style -f ${TMPDIR}/xcsv.geo -o xcsv,style=${TMPDIR}/testo.style -F ${TMPDIR}/xcsv.xcsv
+diff -u ${TMPDIR}/xcsv.geo ${TMPDIR}/xcsv.xcsv
index 193f6f387358a41213c6132590c4a641e604f0ce..6afd9856ba37b89a89b8b858f788e91268b9df92 100644 (file)
@@ -46,10 +46,12 @@ extern ff_vecs_t garmin_vecs;
 extern ff_vecs_t mxf_vecs;
 extern ff_vecs_t holux_vecs;
 extern ff_vecs_t ozi_vecs;
+extern ff_vecs_t xcsv_vecs;
 extern ff_vecs_t tpg_vecs;
 extern ff_vecs_t dna_vecs;
 extern ff_vecs_t magnav_vec;
 extern ff_vecs_t xmap_vecs;
+extern ff_vecs_t xmapwpt_vecs;
 extern ff_vecs_t tmpro_vecs;
 extern ff_vecs_t gpsdrive_vecs;
 
@@ -122,6 +124,12 @@ vecs_t vec_list[] = {
                "Delorme Topo USA4/XMap Conduit", 
                NULL
        },
+       {
+               &xmapwpt_vecs,
+               "xmapwpt",
+               "Delorme XMap HH Native .WPT", 
+               ".wpt"
+       },
        {
                &dna_vecs,
                "dna",
@@ -176,6 +184,12 @@ vecs_t vec_list[] = {
                "OziExplorer Waypoint",
                "ozi"
        },
+       {
+               &xcsv_vecs,
+               "xcsv",
+               "? Character Separated Values",
+               NULL
+       },
        {
                &tpg_vecs,
                "tpg",
diff --git a/gpsbabel/xcsv.c b/gpsbabel/xcsv.c
new file mode 100644 (file)
index 0000000..b03bb14
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+    XCSV - X Character Separated Values (.???)
+
+    A hopefully not too feeble attempt at parsing whatever separated values
+    files into the waypoint structure and back out again.  This is a config-
+    file wrapper around csv_util.c.
+
+    Copyright (C) 2002 Alex Mottram (geo_alexm at cox-internet.com)
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
+
+ */
+
+#include <ctype.h>
+#include "defs.h"
+#include "csv_util.h"
+
+#define MYNAME "XCSV"
+#define ISSTOKEN(a,b) (strncmp(a,b, strlen(b)) == 0)
+
+/* a table of config file constants mapped to chars */
+static 
+char_map_t xcsv_char_table[] = {
+       { "COMMA",              ","     },
+       { "COMMASPACE",         ", "    },
+       { "SINGLEQUOTE",        "'"     },
+       { "DOUBLEQUOTE",        "\""    },
+       { "COLON",              ":"     },
+       { "SEMICOLON",          ";"     },
+       { "NEWLINE",            "\n"    },
+       { "CR",                 "\n"    },
+       { "CRNEWLINE",          "\r\n"  },
+       { "TAB",                "\t"    },
+       { "SPACE",              " "     },
+       { "HASH",               "#"     },
+       { NULL,                 NULL    }
+};     
+
+void
+xcsv_destroy_style(void)
+{
+    queue *elem, *tmp;
+    field_map_t *fmp;
+    ogue_t *ogp;
+
+    /* 
+     * If this xcsv_file struct came from a file we can free it all.
+     * If not, we can at least free the queue elements.
+     */
+
+    /* destroy the prologue */
+    QUEUE_FOR_EACH(&xcsv_file.prologue, elem, tmp) {
+        if (xcsv_file.is_internal == 0) {
+            ogp = (ogue_t *)elem;
+            if (ogp->val)
+                free(ogp->val);
+        }
+        if (elem)
+            free(elem);
+    }
+
+    /* destroy the epilogue */
+    QUEUE_FOR_EACH(&xcsv_file.epilogue, elem, tmp) {
+        if (xcsv_file.is_internal == 0) {
+            ogp = (ogue_t *)elem;
+            if (ogp->val)
+                free(ogp->val);
+        }
+        if (elem)
+            free(elem);
+    }
+
+    /* destroy the ifields */
+    QUEUE_FOR_EACH(&xcsv_file.ifield, elem, tmp) {
+        if (xcsv_file.is_internal == 0) {
+            fmp = (field_map_t *) elem;
+            if (fmp->key)
+                free(fmp->key);
+            if (fmp->val)
+                free(fmp->val);
+            if (fmp->printfc)
+                free(fmp->printfc);
+        }
+        if (elem)
+            free(elem);
+    }
+
+    /* destroy the ofields, if they are not re-mapped to ifields. */
+    if (xcsv_file.ofield != &xcsv_file.ifield) {
+        QUEUE_FOR_EACH(xcsv_file.ofield, elem, tmp) {
+            if (xcsv_file.is_internal == 0) {
+                fmp = (field_map_t *) elem;
+                if (fmp->key)
+                    free(fmp->key);
+                if (fmp->val)
+                    free(fmp->val);
+                if (fmp->printfc)
+                    free(fmp->printfc);
+            }
+            if (elem)
+                free(elem);
+        }
+
+        if (xcsv_file.ofield)
+            free(xcsv_file.ofield);
+    }
+
+    if (xcsv_file.is_internal == 0) {
+        /* other alloc'd glory */
+        if (xcsv_file.field_delimiter)
+            free(xcsv_file.field_delimiter);
+
+        if (xcsv_file.record_delimiter)
+            free(xcsv_file.record_delimiter);
+
+        if (xcsv_file.badchars)
+            free(xcsv_file.badchars);
+    }
+
+    /* return everything to zeros */
+    memset(&xcsv_file, '\0', sizeof(xcsv_file));
+}
+
+static const char *
+get_char_from_constant_table(char *key)
+{
+    char_map_t *cm = xcsv_char_table;
+
+    while ((cm->key) && (strcmp(key, cm->key) != 0)) {
+        cm++;
+    }
+
+    return (cm->chars);
+}
+
+static void
+xcsv_read_style(const char *fname)
+{
+    char sbuff[8192];
+    int i, linecount = 0;
+    FILE *fp;
+    char *s, *p, *sp;
+    const char *cp;
+    char *key, *val, *pfc;
+
+    xcsv_file_init();
+
+    fp = fopen(fname, "r");
+
+    if (!fp)
+        fatal(MYNAME ": Cannot read style file: %s\n", fname);
+
+    do {
+        memset(sbuff, '\0', sizeof(sbuff));
+        fgets(sbuff, sizeof(sbuff), fp);
+        rtrim(sbuff);
+
+        /* 
+         * tokens should be parsed longest to shortest, unless something
+         * requires a previously set value.  This way something like
+         * SHORT and SHORTNAME don't collide.
+         */
+
+        /* whack off any comments */
+        if ((p = strchr(sbuff, '#')) != NULL)
+            *p = '\0';
+
+        if (strlen(sbuff)) {
+            if (ISSTOKEN(sbuff, "FIELD_DELIMITER")) {
+                sp = csv_stringtrim(&sbuff[16], "\"");
+                cp = get_char_from_constant_table(sp);
+                if (cp)
+                    xcsv_file.field_delimiter = xstrdup(cp);
+                else
+                    xcsv_file.field_delimiter = sp;
+            } else
+
+            if (ISSTOKEN(sbuff, "RECORD_DELIMITER")) {
+                sp = csv_stringtrim(&sbuff[17], "\"");
+                cp = get_char_from_constant_table(sp);
+                if (cp)
+                    xcsv_file.record_delimiter = xstrdup(cp);
+                else
+                    xcsv_file.field_delimiter = sp;
+            } else
+
+            if (ISSTOKEN(sbuff, "BADCHARS")) {
+                sp = csv_stringtrim(&sbuff[9], "\"");
+                cp = get_char_from_constant_table(sp);
+                if (cp)
+                    xcsv_file.badchars = xstrdup(cp);
+                else
+                    xcsv_file.badchars = sp;
+            } else
+
+            if (ISSTOKEN(sbuff, "PROLOGUE")) {
+                xcsv_prologue_add(xstrdup(&sbuff[9]));
+            } else
+
+            if (ISSTOKEN(sbuff, "EPILOGUE")) {
+                xcsv_epilogue_add(xstrdup(&sbuff[9]));
+            } else
+
+            if (ISSTOKEN(sbuff, "IFIELD")) {
+                key = val = pfc = NULL;
+                
+                s = csv_lineparse(&sbuff[6], ",", "", linecount);
+
+                i = 0;
+                while (s) {
+                    switch(i) {
+                    case 0:
+                        /* key */
+                        key = csv_stringtrim(s, "\"");
+                        break;
+                    case 1:
+                        /* default value */
+                        val = csv_stringtrim(s, "\"");
+                        break;
+                    case 2:
+                        /* printf conversion */
+                        pfc = csv_stringtrim(s, "\"");
+                        break;
+                    default:
+                        break;
+                    }
+                    i++;
+
+                    s = csv_lineparse(NULL, ",", "", linecount);
+                }
+
+                xcsv_ifield_add(key, val, pfc);
+
+            } else
+
+            /* 
+             * as OFIELDs are implemented as an after-thought, I'll
+             * leave this as it's own parsing for now.  We could
+             * change the world on ifield vs ofield format later..
+             */
+            if (ISSTOKEN(sbuff, "OFIELD")) {
+                key = val = pfc = NULL;
+
+                s = csv_lineparse(&sbuff[6], ",", "", linecount);
+
+                i = 0;
+                while (s) {
+                    switch(i) {
+                    case 0:
+                        /* key */
+                        key = csv_stringtrim(s, "\"");
+                        break;
+                    case 1:
+                        /* default value */
+                        val = csv_stringtrim(s, "\"");
+                        break;
+                    case 2:
+                        /* printf conversion */
+                        pfc = csv_stringtrim(s, "\"");
+                        break;
+                    default:
+                        break;
+                    }
+                    i++;
+                    s = csv_lineparse(NULL, ",", "", linecount);
+                }
+
+                xcsv_ofield_add(key, val, pfc);
+            }
+        }
+    } while (!feof(fp));
+
+    /* if we have no output fields, use input fields as output fields */
+    if (xcsv_file.ofield_ct == 0) {
+        if (xcsv_file.ofield) 
+            free(xcsv_file.ofield);
+        xcsv_file.ofield = &xcsv_file.ifield;
+        xcsv_file.ofield_ct = xcsv_file.ifield_ct;
+    }
+
+    fclose(fp);
+}
+
+static void
+xcsv_rd_init(const char *fname, const char *args)
+{
+    const char *p;
+
+    /* 
+     * if we don't have an internal style defined, we need to
+     * read it from a user-supplied style file, or die trying.
+     */
+    if (xcsv_file.is_internal == 0) {
+        p = get_option(args, "style");
+
+        if (!p)
+            fatal(MYNAME ": XCSV input style not declared.  Use ... -i xcsv,style=path/to/file.style\n");
+
+        xcsv_read_style(p);
+    }
+
+    xcsv_file.xcsvfp = fopen(fname, "r");
+
+    if (xcsv_file.xcsvfp == NULL)
+        fatal(MYNAME ": Cannot open %s for reading\n", fname);
+}
+
+static void
+xcsv_rd_deinit(void)
+{
+    fclose(xcsv_file.xcsvfp);
+
+    xcsv_destroy_style();
+}
+
+static void
+xcsv_wr_init(const char *fname, const char *args)
+{
+    const char * p;
+    
+    /* if we don't have an internal style defined, we need to
+     * read it from a user-supplied style file, or die trying.
+     */
+    if (xcsv_file.is_internal == 0) {
+        p = get_option(args, "style");
+
+        if (!p)
+            fatal(MYNAME ": XCSV output style not declared.  Use ... -o xcsv,style=path/to/file.style\n");
+
+        xcsv_read_style(p);
+
+        /* set mkshort options from the command line */
+        if (global_opts.synthesize_shortnames) {
+            p = get_option(args, "snlen");
+            if (p)
+                setshort_length(atoi(p));
+
+            p = get_option(args, "snwhite");
+            if (p)
+                setshort_whitespace_ok(atoi(p));
+
+            p = get_option(args, "snupper");
+            if (p)
+                setshort_mustupper(atoi(p));
+
+            setshort_badchars(xcsv_file.badchars);
+        }
+    }
+
+    xcsv_file.xcsvfp = fopen(fname, "w");
+
+    if (xcsv_file.xcsvfp == NULL)
+        fatal(MYNAME ": Cannot open %s for writing\n", fname);
+}
+
+static void
+xcsv_wr_deinit(void)
+{
+    fclose(xcsv_file.xcsvfp);
+
+    xcsv_destroy_style();
+}
+
+ff_vecs_t xcsv_vecs = {
+    xcsv_rd_init,
+    xcsv_wr_init,
+    xcsv_rd_deinit,
+    xcsv_wr_deinit,
+    xcsv_data_read,
+    xcsv_data_write,
+};
diff --git a/gpsbabel/xmapwpt.c b/gpsbabel/xmapwpt.c
new file mode 100644 (file)
index 0000000..c1e834f
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+    Delorme XMap HandHeld .WPT Format 
+    (as created by XMapHH Street Atlas/PPC)
+    1296126539:1481466224:1895825408:1392508928:3137157:text:text:text\n
+
+    Contributed to gpsbabel by Alex Mottram (geo_alexm at cox-internet.com)
+
+    Copyright (C) 2002 Robert Lipe, robertlipe@usa.net
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
+
+ */
+
+#include "defs.h"
+#include "csv_util.h"
+
+#define MYNAME "XMAPWPT"
+
+static void
+xmapwpt_set_style()
+{
+    /* set up the xmapwpt xcsv_file struct */
+    xcsv_file_init();
+
+    /* this is an internal style, don't mess with it */
+    xcsv_file.is_internal = 1;
+
+    /* how the file gets split up */
+    xcsv_file.field_delimiter = ":";
+    xcsv_file.record_delimiter = "\n";
+    xcsv_file.badchars = ":";
+
+    xcsv_ifield_add("CONSTANT", "1296126539", "%s");
+    xcsv_ifield_add("CONSTANT", "1481466224", "%s");
+    xcsv_ifield_add("LAT_INT32DEG", "", "%d");
+    xcsv_ifield_add("LON_INT32DEG", "", "%d");
+    xcsv_ifield_add("CONSTANT", "3137157", "%s");
+    xcsv_ifield_add("SHORTNAME", "", "%-.31s");   
+    xcsv_ifield_add("IGNORE", "", "%-.31s");      
+
+    /* 
+     * actual description len accepted is 79. however under win32, we
+     * run the risk of the compiled app ending a line in \r\n when we 
+     * say \n.  This, in turn, overruns a fixed len buffer and causes
+     * XmapHH to die both occasionally and horribly.
+     */
+    xcsv_ifield_add("DESCRIPTION", "", "%-.78s"); 
+
+    /* outfields are infields */
+    if (xcsv_file.ofield)
+        free(xcsv_file.ofield);
+    xcsv_file.ofield = &xcsv_file.ifield;
+    xcsv_file.ofield_ct = xcsv_file.ifield_ct;
+
+    /* set up mkshort */
+    if (global_opts.synthesize_shortnames) {
+        setshort_length(32);
+        setshort_whitespace_ok(0);
+        setshort_badchars(xcsv_file.badchars);
+    }
+}
+
+static void
+xmapwpt_rd_init(const char *fname, const char *args)
+{
+    xmapwpt_set_style();
+
+    xcsv_file.xcsvfp = fopen(fname, "r");
+    
+    if (xcsv_file.xcsvfp == NULL) {
+        fatal(MYNAME ": Cannot open %s for reading\n", fname );
+    }
+}
+
+static void
+xmapwpt_wr_init(const char *fname, const char *args)
+{
+    xmapwpt_set_style();
+
+    xcsv_file.xcsvfp = fopen(fname, "w");
+    
+    if (xcsv_file.xcsvfp == NULL) {
+        fatal(MYNAME ": Cannot open %s for reading\n", fname );
+    }
+}
+
+static void
+xmapwpt_deinit(void)
+{
+    if (xcsv_file.xcsvfp)
+        fclose(xcsv_file.xcsvfp);
+        
+    xcsv_destroy_style();
+}
+
+ff_vecs_t xmapwpt_vecs = {
+    xmapwpt_rd_init,
+    xmapwpt_wr_init,
+    xmapwpt_deinit,
+    xmapwpt_deinit,
+    xcsv_data_read,
+    xcsv_data_write,
+};